package v1

import (
	"encoding/json"
	"gin-wave-im/global"
	"gin-wave-im/middleware"
	"gin-wave-im/model"
	"gin-wave-im/model/response"
	"gin-wave-im/service"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"go.uber.org/zap"
	"gopkg.in/fatih/set.v0"
	"log"
	"net/http"
	"sync"
	"time"
)

//本核心在于形成userid和Node的映射关系
type Node struct {
	Conn *websocket.Conn
	UserId uint64
	//并行转串行,
	DataQueue chan []byte
	GroupSets set.Interface
}

var upGrader = websocket.Upgrader{
	CheckOrigin: func (r *http.Request) bool {
		return true
	},
}

//映射关系表
var clientMap = make(map[uint64]*Node, 0)

//读写锁
var rwLocker sync.RWMutex


func Chat(ctx *gin.Context) {
	token := ctx.Request.URL.Query().Get("token")
	claims, tokenRep, err := middleware.ImAuthMiddleware(token)

	//升级get请求为webSocket协议
	conn, _ := upGrader.Upgrade(ctx.Writer, ctx.Request, nil)

	if err != nil {
		msgRep := response.MsgResponse{
			Cmd: model.CMD_Fail,
			Msg: err.Error(),
			Auth: response.TokenResponse{},
		}
		jsonMsg,_ := json.Marshal(msgRep)
		conn.WriteMessage(websocket.TextMessage, jsonMsg)
		conn.Close()
		return
	}

	userId := claims.ID

	//todo 获得conn
	node := &Node{
		Conn:      conn,
		UserId:    userId,
		DataQueue: make(chan []byte, 100),
		GroupSets: set.New(set.ThreadSafe),
	}

	//todo 获取用户全部群id
	comIds, comErr := service.SearchCommunityIds(userId)
	if comErr == nil {
		for _, v := range comIds {
			node.GroupSets.Add(v)
		}
	}

	//todo userid和node形成绑定关系
	rwLocker.Lock()
	clientMap[userId] = node
	rwLocker.Unlock()

	//todo 完成发送逻辑,con
	go sendProc(node)
	//todo 完成接收逻辑
	go recvProc(node)

	msgRep := response.MsgResponse{
		Cmd: model.CMD_SUCCESS,
		Msg: "连接成功",
		Auth: tokenRep,
	}
	jsonMsg,_ := json.Marshal(msgRep)
	sendMsg(userId, jsonMsg)
}

//ws发送协程
func sendProc(node *Node) {
	for {
		select {
		case data := <-node.DataQueue:
			err := node.Conn.WriteMessage(websocket.TextMessage, data)
			if err != nil {
				log.Println("发送错误",err.Error())
				return
			}
		}
	}
}

//ws接收协程
func recvProc(node *Node) {
	for {
		_, data, err := node.Conn.ReadMessage()
		if err != nil {
			// 客户端退出
			Offline(node.UserId)
			global.GnLog.Error(err.Error(), zap.Any("客户端退出：", err))
			return
		}
		log.Println(clientMap)
		//log.Printf("recv[ws]<=%s\n", data)
		dispatch(data)
		//broadMsg(data)
	}
}

//todo 发送消息
func sendMsg(userId uint64, msg []byte) {
	rwLocker.RLock()
	node, ok := clientMap[userId]
	rwLocker.RUnlock()
	if ok {
		node.DataQueue <- msg
	}
}

//todo 下线
func Offline(userId uint64) {
	log.Println("下线了", userId)
	rwLocker.Lock()
	delete(clientMap, userId)
	rwLocker.Unlock()
	log.Println(clientMap)
}

func dispatch(data []byte) {
	//todo 解析data为message
	msg := model.Message{}
	err := json.Unmarshal(data, &msg)
	if err != nil {
		global.GnLog.Error(err.Error(), zap.Any("err", err))
		return
	}
	msg.CreatedAt = time.Now()

	//todo 根据cmd对逻辑进行处理
	switch msg.Cmd {
	case model.CMD_SINGLE_MSG:
		service.AddMessage(&msg)
		dstId := msg.DstId
		sMsg := model.Message{
			GN_MODEL: model.GN_MODEL{
				ID: msg.ID,
				CreatedAt: msg.CreatedAt,
			},
			Cmd:      msg.Cmd,
			UserId:   msg.DstId,
			DstId:    msg.UserId,
			Media:    msg.Media,
			Content:  msg.Content,
			ThumbPic: msg.ThumbPic,
			Pic:      msg.Pic,
			Url:      msg.Url,
		}
		jsonMsg,_ := json.Marshal(sMsg)
		sendMsg(dstId, jsonMsg)
	case model.CMD_ROOM_MSG:
		service.AddMessage(&msg)
		jsonMsg,_ := json.Marshal(msg)
		//todo 群聊转发逻辑
		for userId, v := range clientMap {
			//排除当前发送用户的所有群id
			if v.GroupSets.Has(msg.DstId) && userId != msg.UserId {
				v.DataQueue <- jsonMsg
			}
		}
	case model.CMD_OFFLINE:
		Offline(msg.UserId)
	case model.CMD_HEART:
		//todo 一般啥都不做
	}
}